package org.example.registry.redis;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.cron.CronUtil;
import cn.hutool.cron.task.Task;
import cn.hutool.json.JSONUtil;
import org.example.Model.ServiceMetaInfo;
import org.example.config.RegistryConfig;
import org.example.constant.RpcConstant;
import org.example.registry.IRegistry;

import java.util.*;

/**
 * @Author Jiangxiaonan
 * @Description 使用redis模拟注册中心
 * @Date 2024/9/15 10:26
 **/
public class RedisRegistry implements IRegistry {

    final RedisOperations operations = new RedisOperations();
    // 根节点
    private static final String ROOT = RpcConstant.DEFAULT_CONFIG_PREFIX;
    // 本机注册的节点key集合，用于维护续期
    private final Set<String> localRegisterNodeKeySet = new HashSet<>();

    @Override
    public void init(RegistryConfig config) {
        heartbeat();
    }

    @Override
    public void register(ServiceMetaInfo serviceMetaInfo) throws Exception {
        // 创建一个KV存储节点
        String registerKey = ROOT + serviceMetaInfo.getServiceKey();
//        operations.set(registerKey, JSONUtil.toJsonStr(serviceMetaInfo));
        operations.setKeyWithExpirationUsingSetEx(registerKey, JSONUtil.toJsonStr(serviceMetaInfo), 300);
        localRegisterNodeKeySet.add(registerKey);
    }

    @Override
    public void deregister(ServiceMetaInfo serviceMetaInfo) {
        String registerKey = ROOT + serviceMetaInfo.getServiceNodeKey();
        operations.delete(registerKey);
    }

    @Override
    public List<ServiceMetaInfo> discovery(String serviceKey) {

        try {
            // 前缀查询
//            String keyValues = operations.get(searchPrefix);
            String keyValues = operations.get(ROOT + serviceKey);
            ArrayList<ServiceMetaInfo> serviceMetaInfos = new ArrayList<>();
            if(keyValues != null) {
                serviceMetaInfos.add(JSONUtil.toBean(keyValues, ServiceMetaInfo.class));
            }
            return serviceMetaInfos;
        } catch (Exception e) {
            throw new RuntimeException("获取服务列表失败", e);
        }
    }

    @Override
    public void destroy() {
        // 停止定时任务
        CronUtil.stop();
        // 清理注册节点
        for (String registerKey : localRegisterNodeKeySet) {
            operations.delete(registerKey);
        }
        localRegisterNodeKeySet.clear();
        operations.destroy();
    }

    //可以使用 Hutool 工具类的 CronUtil 实现定时任务，
    //对所有集合中的节点执行 重新注册 操作，这是一个小 trick，就相当于续签了。
    @Override
    public void heartbeat() {
        CronUtil.schedule("heartbeat", "*/10 * * * * *", new Task() {
            @Override
            public void execute() {
                for (String registerKey : localRegisterNodeKeySet) {
                    try {
                        // 重新注册
                        ServiceMetaInfo serviceMetaInfo = JSONUtil.toBean(operations.get(registerKey), ServiceMetaInfo.class);
                        if (ObjectUtil.isEmpty(serviceMetaInfo)) {
                            continue;
                        }
                        register(serviceMetaInfo);
                    } catch (Exception e) {
                        throw new RuntimeException(registerKey +" 续签失败", e);
                    }
                }
            }
        });
        // 支持秒级别定时任务
        CronUtil.setMatchSecond(true);
        CronUtil.start();
    }
}
